package jmine.tec.executionlog.domain;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.SQLException;

import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;

import jmine.tec.executionlog.domain.enumx.ExecutionStatus;
import jmine.tec.persist.annotation.Alias;
import jmine.tec.persist.annotation.Comment;
import jmine.tec.persist.annotation.Constraint;
import jmine.tec.persist.annotation.Index;

import org.hibernate.Hibernate;
import org.hibernate.annotations.Type;

import bancosys.tec.component.Documentation;
import bancosys.tec.datadigester.input.ResetableInputStreamFactory;
import bancosys.tec.persist.bussobj.PersistableBusinessObject;
import bancosys.tec.persist.exception.PersistenceException;
import bancosys.tec.utils.date.Timestamp;

/**
 * Log de execucao do Data Digester.
 * 
 * @author Marcio Ribeiro (mmr)
 * @author diego.ferreira
 * @created Jun 19, 2008
 */
@Entity
@Table(name = "EXECUTION_LOG")
@Alias("EXELOG")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@SequenceGenerator(name = "SEQ_EXELOG", sequenceName = "SEQ_EXELOG")
@DiscriminatorColumn(name = "TIPO_EXECUCAO", discriminatorType = DiscriminatorType.INTEGER)
@Comment(table = "EXECUTION_LOG", column = "TIPO_EXECUCAO", value = "IDENTIFICA O TIPO DE LOG ARMAZENADO.")
@Constraint(suffix = "1")
@Documentation("ARMAZENA OS LOGS DE EXECUÇÃO")
public abstract class AbstractExecutionLog extends PersistableBusinessObject {

    private Long id;

    private Timestamp executionStartTime;

    private Timestamp executionEndTime;

    private Blob content;

    private ExecutionStatus status;

    private String userName;

    private transient InputStream cachedInputStream;

    /**
     * @return o id.
     */
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_EXELOG")
    @Column(name = "COD_EXECUTION_LOG", nullable = false)
    @Documentation("CODIGO DO LOG DE EXECUÇÃO.")
    public Long getId() {
        return this.id;
    }

    /**
     * Define o id.
     * 
     * @param id o id.
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * @return data/hora do inicio da execucao.
     */
    @Index(suffix = "JT_0")
    @Column(name = "TS_START", nullable = false)
    @Type(type = "bancosys.tec.persist.hibernate.type.TimestampType")
    @Documentation("DATA/HORA DO INÍCIO DA EXECUÇÃO DA INTEGRAÇÃO.")
    public Timestamp getExecutionStartTime() {
        return this.executionStartTime;
    }

    /**
     * Define data/hora do inicio da execucao.
     * 
     * @param executionStartTime data/hora do inicio da execucao.
     */
    public void setExecutionStartTime(Timestamp executionStartTime) {
        this.executionStartTime = executionStartTime;
    }

    /**
     * @return data/hora do fim da execucao.
     */
    @Column(name = "TS_FINISH")
    @Type(type = "bancosys.tec.persist.hibernate.type.TimestampType")
    @Documentation("DATA/HORA DO TÉRMINO DA EXECUÇÃO DA INTEGRAÇÃO.")
    public Timestamp getExecutionEndTime() {
        return this.executionEndTime;
    }

    /**
     * Define data/hora do fim da execucao.
     * 
     * @param executionEndTime data/hora do fim da execucao.
     */
    public void setExecutionEndTime(Timestamp executionEndTime) {
        this.executionEndTime = executionEndTime;
    }

    /**
     * @return conteudo da execucao.
     */
    @Basic(fetch = FetchType.LAZY)
    @Type(type = "bancosys.tec.persist.hibernate.type.UpdatableBlobType")
    @Column(name = "CONTENT", nullable = false, length = 20971520)
    @Documentation("CONTEÚDO DO ARQUIVO DE INTEGRAÇÃO.")
    public Blob getContentBlob() {
        return this.content;
    }

    /**
     * Define conteudo de execucao.
     * 
     * @param blob conteudo da execucao.
     */
    public void setContentBlob(Blob blob) {
        this.content = blob;
    }

    /**
     * Define status.
     * 
     * @param status status.
     */
    public void setStatus(ExecutionStatus status) {
        this.status = status;
    }

    /**
     * @return status da execucao.
     */
    @Column(name = "ST", nullable = false)
    @Documentation("STATUS DA EXECUÇÃO DA INTEGRAÇÃO. 0. EM EXECUÇÃO; 1. OK; 2. ERRO; 3. ALERTAS")
    @Constraint(suffix = "0")
    public ExecutionStatus getStatus() {
        return this.status;
    }

    /**
     * @return valor do campo userName.
     */
    @Column(name = "USERNAME", nullable = false)
    @Documentation("NOME DO USUÁRIO QUE DISPAROU A EXECUÇÃO DA INTEGRAÇÃO.")
    public String getUserName() {
        return this.userName;
    }

    /**
     * Define valor para campo userName.
     * 
     * @param userName valor para campo userName.
     */
    public void setUserName(String userName) {
        this.userName = userName;
    }

    /**
     * @return conteudo da execucao.
     */
    @Transient
    public InputStream getContent() {
        if (this.cachedInputStream == null) {
            this.initCachedInputStream();
        }
        if (this.cachedInputStream != null) {
            try {
                this.cachedInputStream.reset();
            } catch (IOException e) {
                throw new PersistenceException(e);
            }
        }
        return this.cachedInputStream;
    }

    /**
     * Inicializa o InputStream
     */
    public void initCachedInputStream() {
        if (this.getContentBlob() != null) {
            try {
                this.cachedInputStream = ResetableInputStreamFactory.createResetableInputStream(this.getContentBlob().getBinaryStream());
                this.cachedInputStream.mark(Integer.MAX_VALUE);
            } catch (SQLException e) {
                throw new PersistenceException(e);
            }
        }
    }

    /**
     * Define conteudo de execucao.
     * 
     * @param stream conteudo da execucao.
     */
    public void setContent(InputStream stream) {
        try {
            this.setContentBlob(Hibernate.createBlob(ResetableInputStreamFactory.createResetableInputStream(stream)));
        } catch (IOException e) {
            throw new PersistenceException(e);
        }
    }

    /**
     * @return <code>true</code> se status de execucao for RUNNING, <code>false</code> se nao.
     */
    @Transient
    public boolean isRunning() {
        return ExecutionStatus.RUNNING.equals(this.status);
    }

    /**
     * @return <code>true</code> se status de execucao for OK, <code>false</code> se nao.
     */
    @Transient
    public boolean isOk() {
        return ExecutionStatus.OK.equals(this.status);
    }

    /**
     * @return <code>true</code> se status de execucao for NAO OK, <code>false</code> se nao.
     */
    @Transient
    public boolean isNotOk() {
        return ExecutionStatus.NOT_OK.equals(this.status);
    }

    /**
     * @return <code>true</code> se status de execucao for de Alerta, <code>false</code> se nao.
     */
    @Transient
    public boolean isAlert() {
        return ExecutionStatus.ALERT.equals(this.status);
    }

    /**
     * @return tempo de duracao de execucao em mili segundos.
     */
    @Transient
    public long getExecutionDuration() {
        Timestamp ts = this.getExecutionEndTime();
        if (this.isRunning() || ts == null) {
            ts = new Timestamp();
        }
        return ts.getTime() - this.getExecutionStartTime().getTime();
    }

}
